#!/usr/bin/env perl
###########################################################################
#
#   Copyright 2010 American Public Media Group
#
#   This file is part of AIR2.
#
#   AIR2 is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   AIR2 is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with AIR2.  If not, see <http://www.gnu.org/licenses/>.
#
###########################################################################

use strict;
use warnings;
use Test::More tests => 58;
use lib 'tests/search';
use Data::Dump qw( dump );
use AIR2TestUtils;
use AIR2::Config;
use AIR2Test::Source;
use AIR2Test::Project;
use AIR2Test::SrcResponseSet;
use AIR2Test::Organization;
use AIR2Test::Inquiry;
use AIR2Test::User;
use Rose::DBx::Object::Indexed::Indexer;
use Search::Tools::XML;
use JSON;

my $stxml          = Search::Tools::XML->new;
my $debug          = $ENV{PERL_DEBUG} || 0;
my $TEST_ORG_NAME1 = 'testorg1';
my $TEST_ORG_NAME2 = 'testorg2';
my $TEST_USERNAME  = 'ima-test-user';
my $TEST_PROJECT   = 'ima-test-project';
my $TEST_INQ_UUID  = 'testinq12345';
my $TMP_DIR        = Path::Class::dir('/tmp/air2-test/search');
$AIR2::Config::SEARCH_ROOT = $TMP_DIR;

$Rose::DB::Object::Debug          = $debug;
$Rose::DB::Object::Manager::Debug = $debug;

$TMP_DIR->mkpath($debug);
my $xml_dir = $TMP_DIR->subdir('xml/sources');
$xml_dir->mkpath($debug);
my $index_dir = $TMP_DIR->subdir('index/sources');
$index_dir->mkpath($debug);

##################################################################################
## set up test data
##################################################################################

ok( my $project = AIR2Test::Project->new( prj_name => $TEST_PROJECT, ),
    "new project" );

ok( my $project2 = AIR2Test::Project->new(
        prj_name        => $TEST_PROJECT . 2,
        prj_shared_flag => 1,
    ),
    "new project2"
);

ok( $project->load_or_save,  "save project" );
ok( $project2->load_or_save, "save project2" );

ok( my $org1 = AIR2Test::Organization->new(
        org_default_prj_id => $project->prj_id,
        org_name           => $TEST_ORG_NAME1,
        )->load_or_save(),
    "create test org1"
);
ok( my $org2 = AIR2Test::Organization->new(
        org_default_prj_id => $project->prj_id,
        org_name           => $TEST_ORG_NAME2,
        )->load_or_save(),
    "create test org2"
);

#diag("org1 id = " . $org1->org_id);
#diag("org2 id = " . $org2->org_id);

ok( my $user = AIR2Test::User->new(
        user_username   => $TEST_USERNAME,
        user_first_name => 'First',
        user_last_name  => 'Last',
    ),
    "create test user"
);
ok( $user->load_or_save(), "save test user" );

# must do this AFTER we set default_prj_id above
ok( $project->add_project_orgs(
        [   {   porg_org_id          => $org1->org_id,
                porg_contact_user_id => $user->user_id,
            },
            {   porg_org_id          => $org2->org_id,
                porg_contact_user_id => $user->user_id,
            }
        ]
    ),
    "add orgs to project"
);
ok( $project->save(), "write ProjectOrgs" );

ok( my $source = AIR2Test::Source->new( src_username => $TEST_USERNAME ),
    "new source" );
ok( $source->add_emails(
        [ { sem_email => $TEST_USERNAME . '@nosuchemail.org' } ]
    ),
    "add email address"
);
ok( $source->add_organizations( [$org1] ), "add orgs to source" );
ok( $source->load_or_save(), "save source" );

ok( my $source2
        = AIR2Test::Source->new( src_username => $TEST_USERNAME . '2' ),
    "new source"
);
ok( $source2->add_emails(
        [ { sem_email => $TEST_USERNAME . '@really-nosuchemail.org' } ]
    ),
    "add email address"
);
ok( $source2->add_organizations( [$org2] ), "add orgs to source" );
ok( $source2->load_or_save(), "save source2" );

ok( my $inq = AIR2Test::Inquiry->new(
        inq_uuid  => $TEST_INQ_UUID,
        inq_title => 'the color query',
    ),
    "create test inquiry"
);

ok( $inq->add_projects( [ $project, $project2 ] ),
    "add projects to inquiry" );

ok( my $ques
        = AIR2::Question->new( ques_value => 'what is your favorite color' ),
    "new question"
);
ok( $inq->add_questions( [$ques] ), "add question" );
ok( $inq->load_or_save, "save inquiry" );
ok( my $srs = AIR2Test::SrcResponseSet->new(
        srs_src_id => $source->src_id,
        srs_inq_id => $inq->inq_id,
        srs_date   => time(),
    ),
    "new SrcResponseset"
);
ok( my $response = AIR2::SrcResponse->new(
        sr_src_id     => $source->src_id,
        sr_ques_id    => $ques->ques_id,
        sr_orig_value => 'blue is my favorite color',
    ),
    "new response"
);
ok( $srs->add_responses( [$response] ), "add responses" );
ok( $srs->load_or_save(), "save SrcResponseSet" );

ok( my $srs2 = AIR2Test::SrcResponseSet->new(
        srs_src_id => $source2->src_id,
        srs_inq_id => $inq->inq_id,
        srs_date   => time(),
    ),
    "new SrcResponseset"
);
ok( my $response2 = AIR2::SrcResponse->new(
        sr_src_id     => $source2->src_id,
        sr_ques_id    => $ques->ques_id,
        sr_orig_value => 'red is my favorite color',
    ),
    "new response"
);
ok( $srs2->add_responses( [$response2] ), "add responses" );
ok( $srs2->load_or_save(), "save SrcResponseSet" );

# create XML

# must do responses first (TODO allow $source->as_xml to do this automatically)
ok( my $resp_xml = $srs->as_xml(
        { debug => $debug, base_dir => $TMP_DIR->subdir('xml/responses') }
    ),
    "get resp_xml"
);
ok( my $resp_xml2 = $srs2->as_xml(
        { debug => $debug, base_dir => $TMP_DIR->subdir('xml/responses') }
    ),
    "get resp_xml2"
);
ok( AIR2::SearchUtils::write_xml_file(
        $srs->srs_id, $TMP_DIR->subdir('xml/responses'),
        $resp_xml,    $debug
    ),
    "write resp_xml"
);
ok( AIR2::SearchUtils::write_xml_file(
        $srs2->srs_id, $TMP_DIR->subdir('xml/responses'),
        $resp_xml2,    $debug
    ),
    "write resp_xml2"
);

ok( my $xml = $source->as_xml(
        {   debug    => $debug,
            base_dir => $xml_dir,
        }
    ),
    "source->as_xml"
);

# write XML to disk
ok( AIR2::SearchUtils::write_xml_file(
        $source->src_id, $xml_dir, $xml, $debug, $debug
    ),
    "write xml file"
);

ok( my $xml2 = $source2->as_xml(
        {   debug    => $debug,
            base_dir => $xml_dir,
        }
    ),
    "source2->as_xml"
);

# write XML to disk
ok( AIR2::SearchUtils::write_xml_file(
        $source2->src_id, $xml_dir, $xml2, $debug, $debug
    ),
    "write xml file"
);

ok( my $inqxml = $inq->as_xml(
        {   debug    => $debug,
            base_dir => $TMP_DIR->subdir('xml/inquiries')
        }
    ),
    "make inqxml"
);

ok( AIR2::SearchUtils::write_xml_file(
        $inq->inq_id, $TMP_DIR->subdir('xml/inquiries'),
        $inqxml, $debug, $debug
    ),
    "write inqxml file"
);

$debug and diag(`tree $TMP_DIR`);

# create index
is( AIR2TestUtils::create_index(
        invindex => $index_dir,
        config =>
            AIR2::Config->get_app_root->file('etc/search/sources.config'),
        input => $xml_dir,
        debug => $debug,
    ),
    2,
    "create tmp source index with 2 docs in it"
);

is( AIR2TestUtils::create_index(
        invindex => $TMP_DIR->subdir('index/inquiries'),
        config =>
            AIR2::Config->get_app_root->file('etc/search/inquiries.config'),
        input => $TMP_DIR->subdir('xml/inquiries'),
        debug => $debug,
    ),
    1,
    "create tmp inquiries index with 1 doc in it"
);

##########################################################################################
## authz tests
##########################################################################################

ok( my $at  = AIR2TestUtils->new_auth_tkt(), "get auth tkt object" );
ok( my $tkt = AIR2TestUtils->dummy_tkt(),    "get dummy auth tkt" );
ok( my $req = AIR2TestUtils::new_http_request(
        HTTP::Request->new( GET => "/?q=color&air2_tkt=$tkt&c=1&i=sources" )
    ),
    "new HTTP::Request"
);

# defer loading this till after test is compiled so that TMP_DIR is
# correctly recognized by AI2::Config and all the Searchers
require AIR2::SearchServer;
ok( my $resp = AIR2::SearchServer::handler( $at, $req ),
    "SearchServer::handler" );

#dump($resp);

ok( my $json = decode_json( $resp->content ),
    "json decode body of response" );

#dump($json);

is( $json->{unauthz_total}, 2, "dummy has 2 unauthz hits" );
is( $json->{total},         0, "dummy has 0 authz hits" );

# test access to source1
my $src1_authz = encode_json(
    {   user => { type => "A" },
        authz => AIR2::SearchUtils::pack_authz( { $org1->org_id => 1 } )
    }
);
my $src1_tkt = $at->ticket(
    uid     => 'nosuchuser',
    ip_addr => '0.0.0.0',
    data    => $src1_authz
);

$req = AIR2TestUtils::new_http_request(
    HTTP::Request->new( GET => "/?q=color&air2_tkt=$src1_tkt&i=sources" ) );

$resp = AIR2::SearchServer::handler( $at, $req );
$json = decode_json( $resp->content );
is( $json->{unauthz_total}, 2, "src1_tkt has unauthz 2 hits" );
is( $json->{total},         1, "src1_tkt has authz to 1 hit" );
is( $json->{results}->[0]->{src_username},
    $TEST_USERNAME, "one hit == source1" );

#dump($json);

# test access to source2
my $src2_authz = encode_json(
    {   user => { type => "A" },
        authz => AIR2::SearchUtils::pack_authz( { $org2->org_id => 1 } )
    }
);
my $src2_tkt = $at->ticket(
    uid     => 'nosuchuser',
    ip_addr => '0.0.0.0',
    data    => $src2_authz
);

$req = AIR2TestUtils::new_http_request(
    HTTP::Request->new( GET => "/?q=color&air2_tkt=$src2_tkt&i=sources" ) );

$resp = AIR2::SearchServer::handler( $at, $req );
$json = decode_json( $resp->content );
is( $json->{unauthz_total}, 2, "src2_tkt has unauthz 2 hits" );
is( $json->{total},         1, "src2_tkt has authz to 1 hit" );
is( $json->{results}->[0]->{src_username},
    $TEST_USERNAME . '2',
    "one hit == source2"
);

#dump( $json );

# re trac #1495
my $src3_authz = encode_json(
    {   user => { type => "A" },
        authz => AIR2::SearchUtils::pack_authz( { 0 => 1 } )
    }
);
my $src3_tkt = $at->ticket(
    uid     => 'nosuchuser',
    ip_addr => '0.0.0.0',
    data    => $src3_authz
);
$req = AIR2TestUtils::new_http_request(
    HTTP::Request->new( GET => "/?q=color&air2_tkt=$src3_tkt&i=inquiries" ) );

$resp = AIR2::SearchServer::handler( $at, $req );
$json = decode_json( $resp->content );
is( $json->{unauthz_total}, 1, "src3_tkt has unauthz 1 hit" );
is( $json->{total},         1, "src3_tkt has authz to 1 hit" );

#dump($json);

#################################################################
# clean up unless debug is on
#################################################################
if ( !$debug ) {
    $TMP_DIR->rmtree;
}

